/**
 * @HttpEndpoint.java  COPYRIGHT xiaojian
 * 
 * Created on 2016年7月20日 下午5:27:52
 */
package xj.toolkit.netty.transport.endpoint;

import java.net.SocketAddress;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.google.common.base.Preconditions;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import xj.toolkit.netty.handler.codec.http.response.DefaultNettyResponseSource;
import xj.toolkit.netty.handler.codec.http.response.NettyHttpResponseBuilder;
import xj.toolkit.netty.transport.ProtocolConstants;
import xj.toolkit.netty.transport.TransportUtils;
import xj.toolkit.netty.util.NettyConstants;
import xj.toolkit.util.Propertyable;

/**
 * @author <a href="mailto:cxj2000@gmail.com">xiaojian.cao</a>
 *
 */
public abstract class HttpEndpoint<I> implements ServerEndpoint<I> {

	private static final Logger logger = LoggerFactory.getLogger(HttpEndpoint.class);

	private Channel channel = null;

	// -- 是否允许跨域
	private boolean allowCrossRealm = false;

	private boolean keepAlive = false;

	@Override
	public void start() {

	}

	@Override
	public void stop() {

	}

	@Override
	public void send(I msg) {
		Assert.isInstanceOf(HttpMessage.class, msg, "ONLY send ResponseSource.");
		HttpMessage httpMessage = (HttpMessage) msg;
		ChannelFuture future = channel.writeAndFlush(httpMessage);
		if (!keepAlive || !HttpHeaders.isKeepAlive(httpMessage)) {
			// no content
			future.addListener(ChannelFutureListener.CLOSE);
		}
	}

	@Override
	public void messageReceived(ChannelHandlerContext ctx, I msg) {
		TransportUtils.attachSender(msg, this);
	}

	public boolean isAllowCrossRealm() {
		return allowCrossRealm;
	}

	public void setAllowCrossRealm(boolean allowCrossRealm) {
		this.allowCrossRealm = allowCrossRealm;
	}

	public boolean isKeepAlive() {
		return keepAlive;
	}

	public void setKeepAlive(boolean keepAlive) {
		this.keepAlive = keepAlive;
	}

	@Override
	public void setChannel(Channel channel) {
		Preconditions.checkArgument(channel != null);

		this.channel = channel;
	}

	public abstract byte[] makeHttpBody(Object requestBean, I response);

	protected abstract void beforeSend(HttpRequest httpRequest, FullHttpResponse response, Object responseBean);

	@Override
	public void send(Object requestBean, I response) {
		if (logger.isDebugEnabled()) {
			logger.debug("sendback bean [{}] with request[{}]", response, requestBean);
		}

		if (!(requestBean instanceof Propertyable)) {
			throw new IllegalArgumentException("Invalid requestBean, mustbe Propertyable instance.");
		}

		Propertyable propertyable = (Propertyable) requestBean;

		HttpRequest httpRequest = (HttpRequest) propertyable.getProperty(NettyConstants.PROPERTY_ORIGINAL_HTTP_REQUEST);

		DefaultNettyResponseSource source = new DefaultNettyResponseSource();
		source.setStatus(HttpResponseStatus.OK);

		byte[] content = makeHttpBody(requestBean, response);
		if (content == null) {
			content = new byte[0];
		}

		source.setContent(content);
		source.setContentLength(content.length);

		HttpResponse httpResponse = null;
		try {
			httpResponse = NettyHttpResponseBuilder.build(source);
		} catch (Exception e) {
			logger.error("Cannot build HttpResponse, source[" + source + "]", e);
			return;
		}

		if (httpRequest != null) {
			String uuid = httpRequest.headers().get(NettyConstants.HEADER_UUID);
			if (uuid != null) {
				httpResponse.headers().set(NettyConstants.HEADER_UUID, uuid);
			}

			String connection = httpRequest.headers().get(HttpHeaders.Names.CONNECTION);

			if (StringUtils.isNotBlank(connection)) {
				httpResponse.headers().set(HttpHeaders.Names.CONNECTION,
						httpRequest.headers().get(HttpHeaders.Names.CONNECTION));
			}
		}

		// -- 跨域的问题
		if (allowCrossRealm) {
			httpResponse.headers().add("Access-Control-Allow-Origin", "*");
			httpResponse.headers().add("Access-Control-Allow-Headers", "V,uuid,Msg,Content-Type,Used-Base64");
			httpResponse.headers().add("Access-Control-Allow-Methods", "OPTIONS,POST,GET");
		}

		beforeSend(httpRequest, (FullHttpResponse) httpResponse, response);

		ChannelFuture future = channel.writeAndFlush(httpResponse);
		if (!keepAlive || !HttpHeaders.isKeepAlive(httpResponse)) {
			// no content
			future.addListener(ChannelFutureListener.CLOSE);
		}

	}

	@Override
	public String getProtocol() {
		return ProtocolConstants.PROTOCOL_HTTP;
	}

	@Override
	public SocketAddress getRemoteAddress() {
		return channel != null ? channel.remoteAddress() : null;
	}

	@Override
	public SocketAddress getLocalAddress() {
		return channel != null ? channel.localAddress() : null;
	}
	}
